home *** CD-ROM | disk | FTP | other *** search
/ Java Developer's Companion / Java Developer's Companion.iso / Javacup / UNUAM4CS.TAR / unlimited / UNUAM4CS / SliceStack.java < prev    next >
Encoding:
Java Source  |  1996-05-21  |  12.0 KB  |  456 lines

  1. /*
  2.  * @(#)/SliceStack.java    1.4 96/03/31 by Andrew Barclay abb@nuccard.eushc.org
  3.  *
  4.  * Copyright (c) 1995 Andrew B. Barclay All Rights Reserved.
  5.  */
  6.  
  7. // I like to know what I'm importing for sanity checks.
  8. import java.applet.Applet ;
  9. import java.applet.AppletContext ;
  10. import java.awt.Color ;
  11. import java.awt.Dimension ;
  12. import java.awt.Event ;
  13. import java.awt.Graphics ;
  14. import java.awt.Image ;
  15. import java.awt.image.ImageObserver ;
  16. import java.awt.Rectangle ;
  17. import java.util.Enumeration ;
  18.  
  19. import SlicerControl ;
  20. import SlicerDisplay ;
  21. import SlicerDisplayInfo ;
  22. import SlicerDisplayGraphic ;
  23.  
  24. /**
  25.  * An applet that displays stacks (one on top of another)
  26.  * of orthogonal slices from volume images.  It receives the
  27.  * slice data and positioning info from a Volume that is owned
  28.  * by a SlicerControl applet on the same page.  Communication with
  29.  * the SlicerControl is through methods of the SlicerDisplay
  30.  * interface.  If no SlicerControl appears, the applet will wait
  31.  * indefinitely.
  32.  *
  33.  * @see SlicerDisplay
  34.  * @see SlicerControl
  35.  * @see Volume
  36.  *
  37.  * @version    1.4 96/03/31
  38.  * @author     Andrew Barclay
  39.  */
  40.  
  41. public class SliceStack extends Applet implements ImageObserver, SlicerDisplay {
  42.     // init parameters:
  43.     String controlname ;
  44.     int axis ;
  45.     double thickness ;
  46.     double zoom ;
  47.  
  48.     // state info:
  49.     Thread me ;
  50.     SlicerControl control ;
  51.     SlicerDisplayInfo sdi ;
  52.     Image curimage ;
  53.     int imgw = -1, imgzw = -1 ;
  54.     int imgh = -1, imgzh = -1 ;
  55.     double xscale = 1.0 ;
  56.     double yscale = 1.0 ;
  57.     boolean selected = false ;
  58.     Rectangle hashrects[] = new Rectangle[4] ;
  59.     Color hashcolors[] = new Color[4] ;
  60.     Color bordercolor = null ;
  61.  
  62.     // look and feel:
  63.     static final long updateRate = 100 ;
  64.     static final int xpad = 3 ;
  65.     static final int ypad = 3 ;
  66.     static final int hashheight = 8 ;
  67.     static final int highlightwidth = 3 ;
  68.  
  69.     boolean debug = false ;
  70.  
  71.     public String getAppletInfo() {
  72.     return "SliceStack by Andrew Barclay" ;
  73.     }
  74.  
  75.     public String[][] getParameterInfo() {
  76.     String[][] info = {
  77.         {"control",        "string",    "name of SlicerControl applet"},
  78.         {"axis",        "0|1|2",    "X, Y, or Z axis slicing"},
  79.         {"slicethickness",    "double",    "mm"},
  80.         {"zoom",        "double",    "zoom factor"},
  81.     } ;
  82.     return info ;
  83.     }
  84.  
  85.     public void init() {
  86.     // clear all the sizes.
  87.     imgw = imgzw = imgh = imgzh = -1 ;
  88.  
  89.     // bug: this has got to go
  90.     String param = getParameter( "AXIS" ) ;
  91.     axis = Integer.parseInt( param ) ;
  92.     dbg( "init - axis="+axis ) ;
  93.  
  94.     param = getParameter( "SLICETHICKNESS" ) ;
  95.     // bug: should check > 0.0 ?
  96.     thickness = (param == null) ? 0.0 : parseDouble( param ) ;
  97.     dbg( "init - thickness="+thickness ) ;
  98.  
  99.     param = getParameter( "ZOOM" ) ;
  100.     zoom = (param == null) ? 1.0 : parseDouble( param ) ;
  101.     dbg( "init - zoom="+zoom ) ;
  102.  
  103.     controlname = getParameter( "CONTROL" ) ;
  104.     dbg( "init - controlname="+controlname ) ;
  105.  
  106.     // handy to see extent of applet:
  107.     //setBackground( Color.black ) ;
  108.     }
  109.  
  110.     // why is there no parseDouble??
  111.     private double parseDouble( String s ) {
  112.     return (Double.valueOf(s)).doubleValue() ;
  113.     }
  114.  
  115.     // Once the runnable is alive, start looking for a control.
  116.     public void start() {
  117.     // Find the named control on this page.
  118.     getControl() ;
  119.  
  120.     // register this with the control.
  121.     sdi = control.register( this, axis, thickness ) ;
  122.     // bug: need to check for null sdi here, throw exception
  123.  
  124.     // initialize our state
  125.     if( thickness == 0.0 ) {
  126.         thickness = sdi.vsize[2] ;
  127.     }
  128.  
  129.     // make slice display isotropic
  130.     double minsize =
  131.         (sdi.vsize[0] < sdi.vsize[1]) ? sdi.vsize[0] : sdi.vsize[1] ;
  132.     xscale = zoom * sdi.vsize[0]/minsize ;
  133.     yscale = zoom * sdi.vsize[1]/minsize ;
  134.  
  135.     setImage( control.getSlice( this, sdi.position ) ) ;
  136.     }
  137.  
  138.     private synchronized void getControl() {
  139.     // sit here and wait for the control to appear.
  140.     while( (control = (SlicerControl)getApplet(controlname)) == null ) {
  141.         try {
  142.         showStatus( "SliceStack"+axis+" waiting for "+controlname+" to appear..." ) ;
  143.         wait(1000);
  144.         } catch (InterruptedException ex) { }
  145.     }
  146.     }
  147.  
  148.     // I get a NullPointerException every time from 
  149.     // netscape.applet.MozillaAppletContext.getApplet(String).
  150.     // getApplets() works, so I'll roll my own
  151.     private Applet getApplet( String name ) {
  152.     AppletContext ac = getAppletContext() ;
  153.     Enumeration en = ac.getApplets() ;
  154.     while( en.hasMoreElements() ) {
  155.         Applet ap = (Applet)en.nextElement() ;
  156.         //dbg( "applet="+ap ) ;
  157.         if( (ap instanceof SlicerControl)
  158.             && name.equals(ap.getParameter("NAME")) ) {
  159.         dbg( "getApplet: found a SlicerControl named "+name+"!" ) ;
  160.         return ap ;
  161.         }
  162.     }
  163.     //dbg( "getApplets() done." ) ;
  164.     return null ;
  165.     }
  166.  
  167.     // For the SlicerDisplay interface.
  168.     public void stopInYourTracks() {
  169.     // I want to remove my image consumer, or stop the
  170.     // image load thread???
  171.     destroy() ;
  172.     }
  173.  
  174.     // This isn't being called??
  175.     public Dimension preferredSize() {
  176.     dbg( "preferredSize(), zw="+imgzw+", zh="+imgzh ) ;
  177.     if( imgzw < 0 || imgzh < 0 ) {
  178.         return null ;
  179.     } else {
  180.         return new Dimension( imgzw+xpad*2, imgzh+ypad*2 ) ;
  181.     }
  182.     }
  183.  
  184.     // @return currently displayed image.
  185.     public synchronized void setImage(Image img) {
  186.     curimage = img ;
  187.     // By using updateRate here, we give the display a chance to
  188.     // catch up with rapid changes.
  189.     repaint(updateRate) ;
  190.     showPosition() ;
  191.     }
  192.  
  193.     // @return true if we know image size, else false.
  194.     private synchronized boolean checkSize() {
  195.     if( curimage == null ) {
  196.         return false ;
  197.     }
  198.  
  199.     if (imgzw < 0 || imgzh < 0 ) {
  200.         // This should be only place that we check/set these.
  201.         imgw = curimage.getWidth(this) ;
  202.         imgh = curimage.getHeight(this) ;
  203.         if (imgw < 0 || imgh < 0 ) {
  204.         return false ;
  205.         } else {
  206.         imgzw = (int)( 0.5 + xscale * imgw ) ;
  207.         imgzh = (int)( 0.5 + yscale * imgh ) ;
  208.         // doesn't work on anything but Solaris??
  209.         /*
  210.         resize( imgzw+2*xpad, imgzh+2*ypad ) ;
  211.         layout() ;
  212.         */
  213.         }
  214.     }
  215.     return true ;
  216.     }
  217.  
  218.     void paintHashmarks( Graphics gr ) {
  219.     for( int i = 0 ; i < 4 ; i++ ) {
  220.         if( hashcolors[i] != null ) {
  221.         gr.setColor( hashcolors[i] ) ;
  222.         gr.fillRect( hashrects[i].x, hashrects[i].y,
  223.             hashrects[i].width, hashrects[i].height ) ;
  224.         }
  225.     }
  226.     }
  227.  
  228.     // For the SlicerDisplay interface.
  229.     public void updateHashmarks( SlicerDisplayGraphic sdg ) {
  230.     boolean dopaint = false ;
  231.     bordercolor = sdg.bordercolor ;
  232.  
  233.     if (imgzw < 0 || imgzh < 0 ) {
  234.         return ;
  235.     }
  236.  
  237.     if( sdg.xhashcolor != null ) {
  238.         int xpos = xpad + (int)(0.5+sdg.xhashpos*imgzw) ;
  239.         int ypos ;
  240.         int width = (int)(0.5+sdg.xhashwidth*imgzw) ;
  241.         int height = (int)(0.5+sdg.xhashlength*imgzh) ;
  242.  
  243.         ypos = ypad ;
  244.         // If the shape changes, make a new rectangle...
  245.         if( hashrects[0] == null
  246.             || width != hashrects[0].width
  247.             || height != hashrects[0].height ) {
  248.         hashrects[0] = new Rectangle( xpos, ypos, width, height ) ;
  249.         dopaint = true ;
  250.         // otherwise, just move the old one.
  251.         } else if( xpos != hashrects[0].x || ypos != hashrects[0].y ) {
  252.         hashrects[0].move( xpos, ypos ) ;
  253.         dopaint = true ;
  254.         }
  255.         hashcolors[0] = sdg.xhashcolor ;
  256.  
  257.         ypos = ypad + imgzh - height ;
  258.         // If the shape changes, make a new rectangle...
  259.         if( hashrects[1] == null
  260.             || width != hashrects[1].width
  261.             || height != hashrects[1].height ) {
  262.         hashrects[1] = new Rectangle( xpos, ypos, width, height ) ;
  263.         dopaint = true ;
  264.         // otherwise, just move the old one.
  265.         } else if( xpos != hashrects[1].x || ypos != hashrects[1].y ) {
  266.         hashrects[1].move( xpos, ypos ) ;
  267.         dopaint = true ;
  268.         }
  269.         hashcolors[1] = sdg.xhashcolor ;
  270.     }
  271.  
  272.     if( sdg.yhashcolor != null ) {
  273.         int xpos ;
  274.         int ypos = ypad + (int)(0.5+sdg.yhashpos*imgzh) ;
  275.         int width = (int)(0.5+sdg.yhashlength*imgzw) ;
  276.         int height = (int)(0.5+sdg.yhashwidth*imgzh) ;
  277.  
  278.         xpos = xpad ;
  279.         // If the shape changes, make a new rectangle...
  280.         if( hashrects[2] == null
  281.             || width != hashrects[2].width
  282.             || height != hashrects[2].height ) {
  283.         hashrects[2] = new Rectangle( xpos, ypos, width, height ) ;
  284.         dopaint = true ;
  285.         // otherwise, just move the old one.
  286.         } else if( xpos != hashrects[2].x || ypos != hashrects[2].y ) {
  287.         hashrects[2].move( xpos, ypos ) ;
  288.         dopaint = true ;
  289.         }
  290.         hashcolors[2] = sdg.yhashcolor ;
  291.  
  292.         xpos = xpad + imgzw - width ;
  293.         // If the shape changes, make a new rectangle...
  294.         if( hashrects[3] == null
  295.             || width != hashrects[3].width
  296.             || height != hashrects[3].height ) {
  297.         hashrects[3] = new Rectangle( xpos, ypos, width, height ) ;
  298.         dopaint = true ;
  299.         // otherwise, just move the old one.
  300.         } else if( xpos != hashrects[3].x || ypos != hashrects[3].y ) {
  301.         hashrects[3].move( xpos, ypos ) ;
  302.         dopaint = true ;
  303.         }
  304.         hashcolors[3] = sdg.yhashcolor ;
  305.     }
  306.  
  307.     if( dopaint ) {
  308.         /*
  309.          * This should not use repaint.  I need to make a method
  310.          * to set cliprects and do updates in there
  311.          * (damage/repair like IV).
  312.          *
  313.          * By using updateRate here, we give the display a chance
  314.          * to catch up with rapid changes.
  315.          */
  316.         repaint(updateRate) ;
  317.     } else {
  318.         // Don't need a full repaint (e.g. color change).
  319.         paintHashmarks( getGraphics() ) ;
  320.         paintBorder( getGraphics() ) ;
  321.     }
  322.     }
  323.  
  324.     void paintBorder() {
  325.     paintBorder( getGraphics() ) ;
  326.     }
  327.  
  328.     void paintBorder( Graphics gr ) {
  329.     gr.setColor( bordercolor ) ;
  330.     for( int xy = 1 ; xy <= highlightwidth ; xy++ ) {
  331.         gr.drawRect( xy, xy,
  332.         imgzw+2*xpad-2*xy,imgzh+2*ypad-2*xy ) ;
  333.     }
  334.     }
  335.  
  336.     public void paint( Graphics g ) {
  337.     // Must have imgzw and imgzh set before drawing anything.
  338.     if( checkSize() ) {
  339.         // Draw the current image.
  340.         if( xscale == 1.0 && yscale == 1.0 ) {
  341.         g.drawImage( curimage, xpad, ypad, this ) ;
  342.         } else {
  343.         g.drawImage( curimage, xpad, ypad, imgzw, imgzh, this ) ;
  344.         }
  345.  
  346.         paintHashmarks( g ) ;
  347.         paintBorder( g ) ;
  348.     }
  349.     }
  350.  
  351.     public synchronized boolean imageUpdate( Image img, int infoflags,
  352.             int x, int y, int w, int h) {
  353.     if ( curimage == null || img != curimage ) {
  354.         return false ;
  355.     }
  356.  
  357.     boolean ret = true;
  358.     boolean dopaint = false;
  359.     long updatetime = 0;
  360.     if ((infoflags & WIDTH) != 0) {
  361.         dopaint = true ;
  362.         dbg( "SliceStack"+axis+": Got width = " + w ) ;
  363.     }
  364.     if ((infoflags & HEIGHT) != 0) {
  365.         dopaint = true ;
  366.         dbg( "SliceStack"+axis+": Got height = " + h ) ;
  367.     }
  368.     if ((infoflags & (FRAMEBITS | ALLBITS)) != 0) {
  369.         dopaint = true;
  370.         ret = false;
  371.         dbg( "SliceStack"+axis+": Finished" ) ;
  372.     } else if ((infoflags & SOMEBITS) != 0) {
  373.         dopaint = true;
  374.         updatetime = updateRate;
  375.         //dbg( "Got some bits, x=" + x + " y=" + y ) ;
  376.         dbg( "SliceStack"+axis+": Loading..." ) ;
  377.     }
  378.     if ((infoflags & ERROR) != 0) {
  379.         perror( "SliceStack"+axis+": Image load error" ) ;
  380.         ret = false;
  381.     }
  382.  
  383.     if (dopaint) {
  384.         repaint(updatetime);
  385.     }
  386.     return ret;
  387.     }
  388.  
  389.     // Handler for slice selection.
  390.     public boolean keyDown( Event evt, int key ) {
  391.     //dbg( "slice display keyDown" ) ;
  392.     if( control == null || sdi == null || !selected ) {
  393.         return false ;
  394.     }
  395.  
  396.     switch( key ) {
  397.         case Event.UP:
  398.         case 'k':
  399.         case ' ':
  400.         setImage( control.getSlice( this, sdi.position-sdi.vsize[2] ) ) ;
  401.         return true ;
  402.         case Event.DOWN:
  403.         case 'j':
  404.         // BackSpace?
  405.         case 8:
  406.         setImage( control.getSlice( this, sdi.position+sdi.vsize[2] ) ) ;
  407.         return true ;
  408.         default:
  409.         perror( "key press="+key ) ;
  410.         return false ;
  411.     }
  412.     }
  413.  
  414.     public boolean mouseEnter( Event evt, int x, int y ) {
  415.     //dbg( "slice display mouseEnter" ) ;
  416.     requestFocus() ;
  417.     synchronized( this ) {
  418.         selected = true ;
  419.     }
  420.     if( control == null ) {
  421.         return false ;
  422.     } else {
  423.         control.setSelected( this ) ;
  424.         showPosition() ;
  425.         return true ;
  426.     }
  427.     }
  428.  
  429.     public boolean mouseExit( Event evt, int x, int y ) {
  430.     //dbg( "slice display mouseExit" ) ;
  431.     nextFocus() ;
  432.     synchronized( this ) {
  433.         selected = false ;
  434.     }
  435.     return true ;
  436.     }
  437.  
  438.     void showPosition() {
  439.     if( sdi != null ) {
  440.         showStatus( "SliceStack"+sdi.axis+" at "+sdi.position+" mm" ) ;
  441.     }
  442.     }
  443.  
  444.     void dbg( String s ) {
  445.     if( debug ) {
  446.         System.out.println( s ) ;
  447.         showStatus( s ) ;
  448.     }
  449.     }
  450.  
  451.     void perror( String s ) {
  452.     System.err.println( s ) ;
  453.     showStatus( s ) ;
  454.     }
  455. }
  456.